/**
 * @author : weiyao
 * @desc _every
 * @constructor
 * @param {Function} fn 一个函数
 * @return {boolean}
 * @version 1.0.0
 *
 * @summary: a example
 * let ev = arr._every((item, index, data)=>{ return item>0;});
 */
Array.prototype._every = function (fn) {
//   1. 判断他是不是一个函数
  if(typeof fn !== 'function'){
     throw new Error('The arg should a function');
  }
  let _this = this;

  for(let i = 0; i < _this.length; i++){
    let res = fn(_this[i],i,_this);
    if(!res){
       return false;
    }
  }
  return  true;
}

/**
 *
 * @param fn
 * @returns {boolean}
 * @private
 */
Array.prototype._some = function (fn) {
//   1. 判断他是不是一个函数
  if(typeof fn !== 'function'){
    throw new Error('The arg should a function');
  }
  let _this = this;
  for(let i = 0; i< _this.length;i++){
    let  res = fn(_this[i],i ,_this);
    if(res){
      return true;
    }
  }
  return false;
}
/**
 *
 * @param fn
 * @returns {[]}
 * @private
 */
Array.prototype._filter = function (fn) {
  //   1. 判断他是不是一个函数
  if(typeof fn !== 'function'){
    throw new Error('The arg should a function');
  }
  let _this = this;
  let result = [];
  for(let i = 0; i<_this.length; i++){
    let res = fn(_this[i],i,_this);
    if(res){
       result.push(_this[i]);
    }
  }
  return result;
}

/**
 * _forEach
 * @param fn
 * @private
 */
Array.prototype._forEach = function (fn){
   if(typeof fn !== 'function'){
     throw  new Error('The fn not is function,you must be is function');
   }

   let _this = this;

   for(let i = 0;i<_this.length;i++){
      fn(_this[i],i,_this);
   }
}

/**
 *
 * @param fn
 * @returns {[]}
 * @private
 */
Array.prototype._map = function(fn){
  //1. 判断他是不是函数，如果不是函数就new 一个 error
  if(typeof fn !== 'function'){
    throw  new Error('The fn not is function,you must be is function');
  }

  let _this = this;
  let result = [];
  for(let i =0 ;i<_this.length;i++) {
    result.push(fn(_this[i],i,_this));
  }
  return result;
}


Array.prototype._reduce = function(fn,initVal = 0){
  if(typeof fn !== 'function') {
    throw new Error('The fn not is function,you must be is function');
  }

  let _this = this;

  for(let i = 0;i< _this.length;i++){
    initVal = fn(initVal, _this[i], i, _this);
  }
 return initVal;
}


/**
 *
 * @param fn
 * @returns {null|*}
 * @private
 */
Array.prototype._find =  function(fn) {
  if (typeof fn !== 'function') {
    throw new Error('The fn not is function,you must be is function');
  }
  let _this = this;
  for(let i = 0; i<_this.length; i++ ){
    let res =  fn(_this[i],i,_this);
    if(res){
      return _this[i];
    }
  }
  return undefined;
}

/**
 *
 * @param fn
 * @returns {[]}
 * @private
 */
Array.prototype._findAll =  function(fn) {
  if (typeof fn !== 'function') {
    throw new Error('The fn not is function,you must be is function');
  }
  let _this = this;
  let result = [];
  for(let i = 0; i<_this.length; i++ ){
    let res =  fn(_this[i],i,_this);
    if(res){
      result.push(_this[i]);
    }
  }
  return result;
}

/**
 *
 * @param fn
 * @returns {null|number}
 * @private
 */
Array.prototype._findIndex = function(fn){
  if(typeof fn !== 'function') {
    throw new Error('The fn not is function,you must be is function');
    }
  let _this = this;
  for(let i = 0; i< _this.length; i++){
     let res = fn(_this[i],i,_this);
     if(res) {
       return i
     }
  }
  return undefined;
}


/**
 *
 * @param fn
 * @returns {[]}
 * @private
 */
Array.prototype._findAllIndex = function(fn){
  if(typeof fn !== 'function') {
    throw new Error('The fn not is function,you must be is function');
  }
  let _this = this;
  let result = [];
  for(let i = 0; i< _this.length; i++){
    let res = fn(_this[i],i,_this);
    if(res) {
      result.push(i)
    }
  }
  return result;
}


/**
 *
 * @param str
 * @returns {string}
 * @private
 */
Array.prototype._join = function(str){
//    1. 判断这个数组是不是字符串
  if(typeof str === 'function') throw new Error('The str should’t function ');
  if(typeof str === "undefined") throw new Error("The str should't a undefined,you must be give a string,for example ','")
  if(typeof  str !== 'string')  str = ',';
  let _this = this;
  if(_this.length == 0) return  "";
  let s = ''
  for(let i = 0; i< _this.length; i++){
    if(this.length-1=== i){
      s+= _this[i];
    }else {
      s  += _this[i]+str;
    }
  }
 return s;
}

/**
 *
 * @param flag
 * @returns {[]}
 * @private
 */
Array.prototype._reverse = function(flag = false){
  let _this =this;
  let result = [];
   if(!flag){
      for(let i = _this.length - 1; i >= 0; i--){
        result.push(_this[i]);
      }
   }else {
     for(let i = _this.length - 1; i >= 0; i--){
       let s;
       if(typeof this[i] === 'string'){
          s = _this[i].split('')._reverse().join('');
       }else if(typeof  this[i] === 'number'){
          s =Number(_this[i].toString().split('')._reverse().join('')) ;
       }else if( typeof _this[i] === 'boolean') {
         s = _this[i];
       }else {
         s = _this[i];
       }
       result.push(s);
     }
   }
   return result;
}

/**
 *
 * @param value
 * @param indexStart
 * @param indexEnd
 * @returns {Array}
 * @private
 */
Array.prototype._fill = function (value = 0,indexStart =0 ,indexEnd = this.length){
   let  _this = this;
   if(indexStart>indexEnd) {
     indexStart =0,indexEnd =_this.length;
     console.warn('The indexStart not should > indexEnd,we will indexStart=0,indexEnd=this.length');
   }

   for(let i = indexStart ; i < indexEnd; i++){
     _this[i] = value;
   }
   return _this;
}

/**
 *
 * @param value
 * @param startIndex
 * @returns {boolean}
 * @private
 */
Array.prototype._includes = function(value , startIndex = 0){
   if( typeof startIndex != 'number') throw new Error('This must be a number');
   if(startIndex >= this.length || startIndex < 0) return false;
   let _this = this;
   for(let i = startIndex; i < _this.length; i ++) {
     if(_this[i] === value) return  true;
   }
   return  false;
}

/**
 *
 * @returns {Array}
 */
Array.prototype._shuffle = function(){
//  1. 洗牌算法
  let _this = this;
  for(let i = _this.length - 1; i>0 ; i-- ) {
    let j = Math.floor(Math.random()*(i+1));
    [_this[i],_this[j]] = [_this[j],_this[i]];
  }
  return _this;
}


/**
 *O(n2)->O(n)
 * @description 复杂度太高
 * @param value
 * @returns {[]}
 * @private
 */
Array.prototype._sample = function(value=1){
  let _this = this;
  let Indexarr = _this._Index();
  let res = [];
  if(value>_this.length) value = _this.length;
  while (value !==0){
    let i = Math.floor(Math.random()*Indexarr.length);
    let j = Indexarr[i];
    Indexarr.splice(i,1);
    res.push(_this[j]);
    value--;
  }

  return res;
}

/**
 *
 * @returns {[]}
 * @private
 */
Array.prototype._Index = function(){
  let _this = this;
  let number = _this.length;
  let fillArr=Array.from(new Array(number).keys())
  return fillArr;
}

/**
 *将 list 拆分为两个数组：第一个数组其元素都满足predicate迭代函数，
 * 而第二个的所有元素均不能满足predicate迭代函数。 predicate 通过 iteratee 进行转换，以简化速记语法。
 * @param fn
 * @returns {[[], []]}
 * @private
 */
Array.prototype._partition = function(fn){
  if(typeof fn !== 'function')
    throw  new Error('The fn must be a function');
   let _this = this;
   let _left = [],_right =[];
  for(let i = 0; i < _this.length; i++){
     let res = fn(_this[i],i,_this);
     if(!res){
       _left.push(_this[i]);
     }else {
       _right.push(_this[i]);
     }
  }
  return [_left,_right];
}

/**
 *
 * @returns {[]}
 * @private
 */
Array.prototype._compact = function(){
  let _this = this;
  let res = []
  _this.forEach((item,index)=>{
      if(item == false || item == 0 || item == null || item == undefined || Object.is(item,NaN) || item == ""){
      }else {
        res.push(item);
      }
  })
  return res;
}

/**
 *
 * @returns {boolean}
 * @private
 */
Array.prototype._isEmpty = function (){
  let _this = this;
  return _this.length === 0;
}


/**
 *
 * @returns {Array}
 * @private
 */
Array.prototype._rest = function (){
  let _this = this;
  _this.splice(0,1);
  return _this;
}
/**
 *
 * @returns {Array}
 * @private
 */
Array.prototype._initial = function(){
  let _this = this;
  _this.splice(this.length-1,1);
  return _this;
}

/**
 *
 * @param args
 * @returns {[]}
 * @private
 */
Array.prototype._without = function (...args){
  let b = Object.prototype.toString.call(...args) === "[object Number]";
  if(!b) throw new Error('args should a number')
  let set = new Set(args);
  let _this = this;
  let res = [];
  for(let i = 0; i < _this.length;i ++) {
     if(!set.has(_this[i])) {
       res.push(_this[i])
     }
  }
  return res;
}

/**
 *
 * @param args
 * @returns {any[]}
 * @private
 */
Array.prototype._union = function(...args){
  // [...]
  let _this = this;
  return [...new Set([...args,..._this].flat(2))];
}

Array.prototype._difference = function (...args){
  let b = Object.prototype.toString.call(...args) === '[object Array]';
  if(!b){
    throw new Error('args  should a Array') ;
  }
  let set = new Set(...args);
  let _this = this;
  let res = [];
  for(let i = 0; i < _this.length;i ++) {
    if(!set.has(_this[i])) {
      res.push(_this[i])
    }
  }
  return res;
}
/**
 *
 * @param value
 * @returns {[]}
 * @private
 */
Array.prototype._range = function(value = 0){
     let _this = this;
//     1.获取这个数组的长度
     let len = _this.length;
    let result = [];
     for(let i  = value; i < len + value;i++){
       result.push(i);
     }
     return result;
}

/**
 *
 * @param size
 * @return {*[]}
 * @private
 */
Array.prototype._dropRight = function(size){
  let _this = this;
  return _this._filter((value,index)=>{
    return index<_this.length-size;
  })
}



/**
 *
 * @param Start
 * @param End
 * @return {[]|*[]}
 * @private
 */
Array.prototype._slice = function (Start = 0,End = this.length){
  let _this = this;
  if(Start<0) Start = 0;
  if(Start>this.length) return [];
  if(Start>End) End = _this.length;
  if(End>=_this.length) End = _this.length;
  let result = [];
  for(let i = Start;i< End; i ++) {
    result._push(_this[i]);
  }
  return result;
}


Array.prototype._concat =function (...args){
    let _this = this;
     let res = _this;
    args._forEach((item,index)=>{
       if(Array.isArray(item)){
         res._push(...item);
       }else {
         res._push(item);
       }
    });
    return res;
}

/**
 *
 * @param args
 * @return {Array}
 * @private
 */
Array.prototype._push = function (...args){
  let _this = this;
  for(let i = 0; i < args.length; i++){
    _this[this.length] = args[i];
  }
  return _this;
}




module.exports  = Array;